﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using LFSRecord2.Model.Undo;
using LFSRecord2.Structures;

namespace LFSRecord2.Model
{
    /// <summary>
    ///  Base class for layer properties
    /// </summary>
    public abstract class CompLayerPropertyBase : INotifyPropertyChanged, IDisposable
    {
        public bool Initializing { get; set; }
        public CompLayerBase ParentLayer { get; protected set; }
        public int PropertyIndex { get; protected set; }
        public string PropertyName { get; protected set; }
        public LayerPropertyValueTypes PropertyValueType { get; protected set; }
        public LayerPropertyValueMeaning PropertyValueMeaning { get; protected set; }

        public bool IsEdited = true; // true because after creation of a project, we must store all properties, not just selectively the ones that are edited.

        public bool IsEditable { get; protected set; }

        private bool _isSelected = false;
        public bool IsSelected
        {
            get { return (bool)_isSelected; }
            set
            {
                bool oldValue = _isSelected;
                _isSelected = value;
                if (oldValue != _isSelected)
                {
                    OnPropertyChanged("IsSelected");
                }
            }
        }

        protected bool _isAnimated = false;
        public bool IsAnimated
        {
            get { return (bool)_isAnimated; }
            set
            {
                if (!IsEditable || HasBoundProperty)
                    return;

                _isAnimated = value;

                if (!Initializing)
                {
                    if (!_isAnimated)
                    {
                        DeleteAllKeyframes();
                    }
                    else
                    {
                        if (KeyFrames.Count == 0)
                            SetKeyAtCurrentTime(LfsRecordController.FrameIndex);
                    }
                    LfsRecordController.project.DeselectAll();
                    if (ParentLayer != null)
                        ParentLayer.IsSelected = true;
                    IsSelected = true;
                }
                SetHasPreFrames();
                SetHasPostFrames();
                SetHasCurrentFrame();
                
                OnPropertyChanged("IsAnimated");
            }
        }

        public bool HasSelectedKeyFrames = false;

        bool _hasPreFrames = false;
        public bool HasPreFrames
        {
            get { return (bool)_hasPreFrames; }
            set
            {
                bool oldValue = _hasPreFrames;
                _hasPreFrames = value;
                if (oldValue != _hasPreFrames)
                    OnPropertyChanged("HasPreFrames");
            }
        }
        bool _hasPostFrames = false;
        public bool HasPostFrames
        {
            get { return (bool)_hasPostFrames; }
            set
            {
                bool oldValue = _hasPostFrames;
                _hasPostFrames = value;
                if (oldValue != _hasPostFrames)
                    OnPropertyChanged("HasPostFrames");
            }
        }
        bool _hasCurrentFrame = false;
        public bool HasCurrentFrame
        {
            get { return (bool)_hasCurrentFrame; }
            set
            {
                bool oldValue = _hasCurrentFrame;
                _hasCurrentFrame = value;
                if (oldValue != _hasCurrentFrame)
                    OnPropertyChanged("HasCurrentFrame");
            }
        }

        public bool HasValueLock = false;
        bool _valueLock = false;
        public bool ValueLock
        {
            get { return _valueLock; }
            set
            {
                bool oldValue = _valueLock;
                _valueLock = value;
                if (oldValue != _valueLock)
                    OnPropertyChanged("ValueLock");
            }
        }

        CompLayerPropertyBase _boundProperty = null;
        public CompLayerPropertyBase BoundProperty
        {
            get { return _boundProperty; }
            set
            {
                BoundPropertyIgnoreParent = false;
                if (value != null)
                {
                    if (IsAnimated)
                        IsAnimated = false;

                    // Add current value listener
                    HandleNewPropertyBinding(value);
                }
                else
                {
                    // Remove current value listener
                    RemoveCurrentValueListener();
                }
                _boundProperty = value;
                OnPropertyChanged("BoundProperty");
                OnPropertyChanged("HasBoundProperty");
            }
        }
        public LayerPropIndex BindingPropertyIndex
        {
            get
            {
                if (_boundProperty == null)
                    return new LayerPropIndex { LayerIndex = (short)-1, PropertyIndex = (sbyte)-1 };
                else
                    return new LayerPropIndex { LayerIndex = (short)_boundProperty.ParentLayer.LayerIndex, PropertyIndex = (sbyte)_boundProperty.PropertyIndex };
            }
        }
        public bool HasBoundProperty
        {
            get 
            {
                return (_boundProperty != null);
            }
        }
        private bool _boundPropertyIgnoreParent = false;
        public bool BoundPropertyIgnoreParent
        {
            get { return _boundPropertyIgnoreParent; }
            set
            {
                _boundPropertyIgnoreParent = value;
                OnPropertyChanged("BoundPropertyIgnoreParent");
            }
        }

        List<CustomKeyFrame> _keyFrameStore = new List<CustomKeyFrame>();
        SortableObservableCollection<CustomKeyFrame> _keyFrames;
        public SortableObservableCollection<CustomKeyFrame> KeyFrames
        {
            get
            {
                if (_keyFrames == null)
                    _keyFrames = new SortableObservableCollection<CustomKeyFrame>(_keyFrameStore);
                return _keyFrames;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                ParentLayer = null;

                for (int a = 0; a < KeyFrames.Count; a++)
                {
                    KeyFrames[a].Dispose();
                }
                KeyFrames.Clear();
                PropertyChanged = null;
            }
        }

        public abstract void AddKeyFrame(CustomKeyFrame key);
        public abstract void AddKeyFrame(CustomKeyFrame key, bool autoBezier);
        public abstract CustomKeyFrame SetKeyAtCurrentTime(int frame);
        public abstract object GetValueAtFrame(int frame);
        public abstract CustomKeyFrame OnCurrentValueChange();
        public abstract bool SetCurrentValue(int frame);
        public abstract void DeleteAllKeyframes();
        public abstract void HandleNewPropertyBinding(CompLayerPropertyBase prop);
        public abstract void RemoveCurrentValueListener();
        public abstract PropertyDataBase DataStructure();
        
        public void RewriteKeyFrameIndices()
        {
            for (int a = 0; a < KeyFrames.Count; a++)
            {
                KeyFrames[a].KeyFrameIndex = a;
            }

            for (int a = 0; a < KeyFrames.Count; a++)
            {
                if (a == 0)
                    KeyFrames[a].Options |= CustomKeyFrameOptions.FirstKey;
                else
                    KeyFrames[a].Options &= ~CustomKeyFrameOptions.FirstKey;
                if (a == KeyFrames.Count - 1)
                    KeyFrames[a].Options |= CustomKeyFrameOptions.LastKey;
                else
                    KeyFrames[a].Options &= ~CustomKeyFrameOptions.LastKey;
            }
        }

        public void PostProcess()
        {
            if (KeyFrames.Count == 0)
            {
                SetHasPreFrames();
                SetHasPostFrames();
                SetHasCurrentFrame();
                if (PropertyValueType == LayerPropertyValueTypes.Point3D)
                {
                    CompLayerPropertyPoint3D prop = (CompLayerPropertyPoint3D)this;
                    prop.clearPoint3DFrameCache();
                }
            }
            else
            {
                SortKeyFrames();
                RewriteKeyFrameIndices();
                SetHasPreFrames();
                SetHasPostFrames();
                SetHasCurrentFrame();
                if (PropertyValueType == LayerPropertyValueTypes.Point3D && ((CompLayerPropertyPoint3D)this).HasAnimationPath)
                {
                    CompLayerPropertyPoint3D prop = (CompLayerPropertyPoint3D)this;
                    prop.AutoBezierWholePath();
                    prop.populatePoint3DCache();
                }
            }
        }

        public CustomKeyFrame GetKeyAtTime(int frame)
        {
            foreach (CustomKeyFrame kf in KeyFrames)
            {
                if (kf.FrameNum == frame)
                    return kf;
            }
            return null;
        }

        public int FrameNumOfPreviousKey()
        {
            CustomKeyFrame kfPrev = null;

            foreach (CustomKeyFrame kf in KeyFrames)
            {
                if (kf.FrameNum >= LfsRecordController.FrameIndex)
                {
                    return (kfPrev == null) ? -1 : kfPrev.FrameNum;
                }
                else
                {
                    kfPrev = kf;
                }
            }

            if (kfPrev != null && kfPrev.FrameNum < LfsRecordController.FrameIndex)
                return kfPrev.FrameNum;

            return -1;
        }

        public int FrameNumOfNextKey()
        {
            foreach (CustomKeyFrame kf in KeyFrames)
            {
                if (kf.FrameNum > LfsRecordController.FrameIndex)
                {
                    return kf.FrameNum;
                }
            }

            return -1;
        }

        public void SetHasPreFrames()
        {
            if (KeyFrames.Count > 0 && KeyFrames[0].FrameNum < LfsRecordController.FrameIndex)
                HasPreFrames = true;
            else
                HasPreFrames = false;
        }

        public void SetHasPostFrames()
        {
            if (KeyFrames.Count > 0 && KeyFrames[KeyFrames.Count - 1].FrameNum > LfsRecordController.FrameIndex)
                HasPostFrames = true;
            else
                HasPostFrames = false;
        }

        public void SetHasCurrentFrame()
        {
            foreach (CustomKeyFrame kf in KeyFrames)
            {
                if (kf.FrameNum == LfsRecordController.FrameIndex)
                {
                    HasCurrentFrame = true;
                    return;
                }
                else if (kf.FrameNum > LfsRecordController.FrameIndex)
                    break;
            }
            HasCurrentFrame = false;
        }

        public void selectAllKeyFrames()
        {
            foreach (CustomKeyFrame kf in KeyFrames)
            {
                if (!kf.IsSelected)
                    kf.IsSelected = true;
            }
        }

        public void SortKeyFrames()
        {
            KeyFrames.Sort(x => x.FrameNum, ListSortDirection.Ascending);
        }

        public CustomKeyFrame ReinstateKeyFrame(KeyFrameDataBase kfData)
        {
            KeyFramePoint3DData kfpData;
            CustomPoint3DKeyFrame kfp;

            KeyFrameDoubleData kfdData;
            CustomDoubleKeyFrame kfd;

            KeyFrameStringData kfsData;
            CustomStringKeyFrame kfs;

            KeyFrameRDData kfrData;
            CustomRDKeyFrame kfr;

            if (kfData.KeyFrameType == CustomKeyFrameTypes.Point3D)
            {
                kfpData = (KeyFramePoint3DData)kfData;
                kfp = new CustomPoint3DKeyFrame(kfpData.FrameNum, kfpData.Value);
                kfp.IsSelected = kfpData.IsSelected;
                //kfp.KeyFrameIndex = kfpData.KeyFrameIndex;
                kfp.EaseInValue = kfpData.EaseInValue;
                kfp.EaseOutValue = kfpData.EaseOutValue;
                kfp.InterpolationTypeIn = kfpData.InterpolationTypeIn;
                kfp.InterpolationTypeOut = kfpData.InterpolationTypeOut;
                kfp.Options = kfpData.Options;
                kfp.DistanceToNextKeyFrame = kfpData.DistanceToNextKeyFrame;
                kfp.BezierTime = kfpData.BezierTime;
                kfp.BezierControlIn = kfpData.BezierControlIn;
                kfp.BezierControlOut = kfpData.BezierControlOut;
                AddKeyFrame(kfp, false);
                return kfp;
            }
            else if (kfData.KeyFrameType == CustomKeyFrameTypes.Double)
            {
                kfdData = (KeyFrameDoubleData)kfData;
                kfd = new CustomDoubleKeyFrame(kfdData.FrameNum, kfdData.Value);
                kfd.IsSelected = kfdData.IsSelected;
                //kfd.KeyFrameIndex = kfdData.KeyFrameIndex;
                kfd.EaseInValue = kfdData.EaseInValue;
                kfd.EaseOutValue = kfdData.EaseOutValue;
                kfd.InterpolationTypeIn = kfdData.InterpolationTypeIn;
                kfd.InterpolationTypeOut = kfdData.InterpolationTypeOut;
                kfd.Options = kfdData.Options;
                kfd.DistanceToNextKeyFrame = kfdData.DistanceToNextKeyFrame;
                kfd.BezierTime = kfdData.BezierTime;
                AddKeyFrame(kfd);
                return kfd;
            }
            else if (kfData.KeyFrameType == CustomKeyFrameTypes.String)
            {
                kfsData = (KeyFrameStringData)kfData;
                kfs = new CustomStringKeyFrame(kfsData.FrameNum, kfsData.Value);
                kfs.IsSelected = kfsData.IsSelected;
                //kfs.KeyFrameIndex = kfsData.KeyFrameIndex;
                kfs.EaseInValue = kfsData.EaseInValue;
                kfs.EaseOutValue = kfsData.EaseOutValue;
                kfs.InterpolationTypeIn = kfsData.InterpolationTypeIn;
                kfs.InterpolationTypeOut = kfsData.InterpolationTypeOut;
                kfs.Options = kfsData.Options;
                kfs.DistanceToNextKeyFrame = kfsData.DistanceToNextKeyFrame;
                AddKeyFrame(kfs);
                return kfs;
            }
            else if (kfData.KeyFrameType == CustomKeyFrameTypes.RDIdent)
            {
                kfrData = (KeyFrameRDData)kfData;
                kfr = new CustomRDKeyFrame(kfrData.FrameNum, kfrData.Value);
                kfr.IsSelected = kfrData.IsSelected;
                //kfr.KeyFrameIndex = kfrData.KeyFrameIndex;
                kfr.EaseInValue = kfrData.EaseInValue;
                kfr.EaseOutValue = kfrData.EaseOutValue;
                kfr.InterpolationTypeIn = kfrData.InterpolationTypeIn;
                kfr.InterpolationTypeOut = kfrData.InterpolationTypeOut;
                kfr.Options = kfrData.Options;
                kfr.DistanceToNextKeyFrame = kfrData.DistanceToNextKeyFrame;
                AddKeyFrame(kfr);
                return kfr;
            }
            return null;
        }
    }
}
